//
//  DMBaseAPIManager.m
//  DaMaoMall
//
//  Created by HXHL-wally on 16/8/2.
//  Copyright © 2016年 daMao-wally. All rights reserved.
//

#import "DMBaseAPIManager.h"
#import "DMAPIProxy.h"
#import "DMAuthParamsGenerator.h"
NSString * const kDMAPIBaseManagerRequestId = @"xyz.ypli.kDMAPIBaseManagerRequestID";

#define DMLoadRequest(REQUEST_METHOD, REQUEST_ID)                                                  \
{\
__weak typeof(self) weakSelf = self;\
REQUEST_ID = [[DMAPIProxy sharedInstance] load##REQUEST_METHOD##WithParams:apiParams useJSON:self.isRequestUsingJSON host:self.host path:self.child.path apiVersion:self.child.apiVersion success:^(DMResponseModel *response) {\
__strong typeof(weakSelf) strongSelf = weakSelf;\
[strongSelf dataDidLoad:response];\
} fail:^(DMResponseError *error) {\
__strong typeof(weakSelf) strongSelf = weakSelf;\
[strongSelf dataLoadFailed:error];\
}];\
[self.requestIdList addObject:@(REQUEST_ID)];\
}\

@interface DMBaseAPIManager()
@property (nonatomic, strong, readwrite) id rawData;
@property (nonatomic, assign, readwrite) BOOL isLoading;
@property (nonatomic, assign) BOOL isNativeDataEmpty;
@property (nonatomic, strong) NSMutableArray *requestIdList;

@property (nonatomic, weak) DMBaseAPIManager<DMAPIManager> *child;

@end

@implementation DMBaseAPIManager

- (instancetype)init {
    self = [super init];
    if (self) {
        if ([self conformsToProtocol:@protocol(DMAPIManager)]) {
            self.child = (id <DMAPIManager>)self;
        } else {
            @throw [NSException exceptionWithName:[NSString stringWithFormat:@"%@ init failed",[self class]]
                                           reason:@"Subclass of DMAPIBaseManager should implement <DMAPIManager>"
                                         userInfo:nil];
        }
    }
    return self;
}

//- (BOOL)shouldCache {
//    return kYLShouldCacheDefault;
//}

- (NSString *)host {
    return kServerURL;
}

- (DMRequestType)requestType {
    return DMRequestTypePost;
}

- (BOOL)isResponseJSONable {
    return YES;
}

- (BOOL)isRequestUsingJSON {
    return YES;
}

- (BOOL)isReachable {
    return [[DMAPIProxy sharedInstance] isReachable];
}

- (BOOL)isLoading {
    if (self.requestIdList.count == 0) {
        _isLoading = NO;
    }
    return _isLoading;
}

- (NSInteger)loadData {
    NSDictionary *params = [self.dataSource paramsForAPI:self];
    NSInteger requestId = [self loadDataWithParams:params];
    return requestId;
}

// 不将此方法开放出去是为了强制使用dataSource来提供数据，类同UITableView
- (NSInteger)loadDataWithParams:(NSDictionary *)params {
    NSInteger requestId = 0;
    NSDictionary *apiParams = [self reformParams:params];
    if ([self shouldLoadRequestWithParams:params]) {
        if ([[DMAPIProxy sharedInstance] isReachable]) {
            self.isLoading = YES;
            NSMutableDictionary *finalAPIParams = [[NSMutableDictionary alloc] init];
            if (params) {
                [finalAPIParams addEntriesFromDictionary:apiParams];
            }
            if (self.child.isAuth) {
                NSDictionary *authParams = [DMAuthParamsGenerator authParams];
                if (authParams) {
                    [finalAPIParams addEntriesFromDictionary:authParams];
                } else {
                    [self dataLoadFailed:
                     [DMBaseAPIManager errorWithRequestId:requestId
                                                   status:DMAPIManagerResponseStatusNeedLogin]];
                }
            }
            switch (self.child.requestType) {
                case DMRequestTypeGet:
                    DMLoadRequest(GET, requestId);
                    break;
                case DMRequestTypePost:
                    DMLoadRequest(POST, requestId);
                    break;
                default:
                    break;
            }
            NSMutableDictionary *params = [apiParams mutableCopy];
            params[kDMAPIBaseManagerRequestId] = @(requestId);
            [self afterLoadRequestWithParams:params];
            return requestId;
        } else {
            [self dataLoadFailed:
             [DMBaseAPIManager errorWithRequestId:requestId
                                           status:DMAPIManagerResponseStatusNoNetwork]];
            return requestId;
        }
    }
    return requestId;
}



#pragma mark - api callbacks
- (void)dataDidLoad:(DMResponseModel *)responseModel {
    
    self.isLoading = NO;
    [self removeRequestIdWithRequestId:responseModel.requestId];
    
    if ([self isResponseJSONable]) {
        NSError *error;
        NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:responseModel.responseData
                                                                 options:NSJSONReadingMutableLeaves
                                                                   error:&error];
        if (error != nil) {
            [self dataLoadFailed:
             [DMBaseAPIManager errorWithRequestId:responseModel.requestId
                                           status:DMAPIManagerResponseStatusParsingError]];
            return;
        }
        
        self.rawData = jsonDict;
        NSInteger status = [jsonDict[@"status"] integerValue];
        if (status != DMAPIManagerResponseStatusSuccess) {
            DMResponseError *error = [DMBaseAPIManager errorWithRequestId:responseModel.requestId
                                                                   status:status
                                                                    extra:jsonDict[@"message"]];
            
            [self.delegate apiManagerLoadDataFail:error];
        }
    } else {
        self.rawData = [responseModel.responseData copy];
    }
    
    if([self isResponseDataCorrect:responseModel]) {
        if ([self beforePerformSuccessWithResponseModel:responseModel]) {
            [self.delegate apiManagerLoadDataSuccess:self];
        }
        [self afterPerformSuccessWithResponseModel:responseModel];
    } else {
        [self.delegate apiManagerLoadDataFail:
         [DMBaseAPIManager errorWithRequestId:responseModel.requestId
                                       status:DMAPIManagerResponseStatusParsingError]];
    }
    
}

- (void)dataLoadFailed:(DMResponseError *)error {
    [self removeRequestIdWithRequestId:error.requestId];
    if ([self beforePerformFailWithResponseModel:error]) {
        [self.delegate apiManagerLoadDataFail:error];
    }
    [self afterPerformFailWithResponseModel:error];
}


#pragma mark - public API
- (void)cancelAllRequests {
    [[DMAPIProxy sharedInstance] cancelRequestWithRequestIdList:self.requestIdList];
    [self.requestIdList removeAllObjects];
}

- (void)cancelRequestWithRequestId:(NSInteger)requestID {
    [self removeRequestIdWithRequestId:requestID];
    [[DMAPIProxy sharedInstance] cancelRequestWithRequestId:@(requestID)];
}

- (id)fetchData {
    return [self fetchDataWithReformer:nil];
}

- (id)fetchDataWithReformer:(id<DMAPIManagerDataReformer>)reformer {
    id resultData = nil;
    if ([reformer respondsToSelector:@selector(apiManager:reformData:)]) {
        resultData = [reformer apiManager:self reformData:self.rawData];
    } else {
        resultData = [self.rawData mutableCopy];
    }
    return resultData;
}

- (id)fetchDataFromModel {
    @throw [NSException exceptionWithName:[NSString stringWithFormat:@"%@ fetchDataFromModel failed",[self class]]
                                   reason:@"Subclass of DMAPIBaseManager should override fetchDataFromModel."
                                 userInfo:nil];
}

#pragma mark - private API
- (void)removeRequestIdWithRequestId:(NSInteger)requestId {
    NSNumber *requestIdToRemove = nil;
    for (NSNumber *storedRequestId in self.requestIdList) {
        if ([storedRequestId integerValue] == requestId) {
            requestIdToRemove = storedRequestId;
        }
    }
    if (requestIdToRemove) {
        [self.requestIdList removeObject:requestIdToRemove];
    }
}

- (NSDictionary *)reformParams:(NSDictionary *)params {
    IMP childIMP = [self.child methodForSelector:@selector(reformParams:)];
    IMP selfIMP = [self methodForSelector:@selector(reformParams:)];
    
    if (childIMP == selfIMP) {
        return params;
    } else {
        NSDictionary *result = nil;
        result = [self.child reformParams:params];
        if (result) {
            return result;
        } else {
            return params;
        }
    }
}

#define DMResponseErrorWithMSG(MSG) DMResponseError(MSG, status, requestId)
+ (DMResponseError *)errorWithRequestId:(NSInteger)requestId
                                 status:(DMAPIManagerResponseStatus)status {
    return [self errorWithRequestId:requestId status:status extra:nil];
}
+ (DMResponseError *)errorWithRequestId:(NSInteger)requestId
                                 status:(DMAPIManagerResponseStatus)status
                                  extra:(NSString *)message {
    switch (status) {
        case DMAPIManagerResponseStatusParsingError:
            return DMResponseErrorWithMSG(@"数据解析错误");
        case DMAPIManagerResponseStatusTimeout:
            return DMResponseErrorWithMSG(@"请求超时");
        case DMAPIManagerResponseStatusNoNetwork:
            return DMResponseErrorWithMSG(@"当前网络已断开");
        case DMAPIManagerResponseStatusNeedLogin:
            return DMResponseErrorWithMSG(@"请登录");
        case DMAPIManagerResponseStatusRequestError:
            return DMResponseErrorWithMSG(@"参数错误");
        case DMAPIManagerResponseStatusTypeServerCrash:
            return DMResponseErrorWithMSG(@"服务器出错");
        case DMAPIManagerResponseStatusTypeServerMessage:
            return DMResponseErrorWithMSG(message?:@"未知信息");
        default:
            return DMResponseErrorWithMSG(@"未知错误");
    }
}

#pragma mark -
- (BOOL)beforePerformSuccessWithResponseModel:(DMResponseModel *)responseModel {
    BOOL result = YES;
    if (self != self.interceptor
        && [self.interceptor respondsToSelector:@selector(apiManager:beforePerformSuccessWithResponseModel:)]) {
        result = [self.interceptor apiManager:self beforePerformSuccessWithResponseModel:responseModel];
    }
    return result;
}
- (void)afterPerformSuccessWithResponseModel:(DMResponseModel *)responseModel {
    if (self != self.interceptor
        && [self.interceptor respondsToSelector:@selector(apiManager:afterPerformSuccessWithResponseModel:)]) {
        [self.interceptor apiManager:self afterPerformSuccessWithResponseModel:responseModel];
    }
}

- (BOOL)beforePerformFailWithResponseModel:(DMResponseError *)responseModel {
    BOOL result = YES;
    if (self != self.interceptor
        && [self.interceptor respondsToSelector:@selector(apiManager:beforePerformFailWithResponseModel:)]) {
        result = [self.interceptor apiManager:self beforePerformFailWithResponseModel:responseModel];
    }
    return result;
}
- (void)afterPerformFailWithResponseModel:(DMResponseError *)responseModel {
    if (self != self.interceptor
        && [self.interceptor respondsToSelector:@selector(apiManager:afterPerformFailWithResponseModel:)]) {
        [self.interceptor apiManager:self afterPerformFailWithResponseModel:responseModel];
    }
}

- (BOOL)shouldLoadRequestWithParams:(NSDictionary *)params {
    BOOL result = YES;
    if (self != self.interceptor
        && [self.interceptor respondsToSelector:@selector(apiManager:shouldLoadRequestWithParams:)]) {
        result = [self.interceptor apiManager:self shouldLoadRequestWithParams:params];
    }
    return result;
}

- (void)afterLoadRequestWithParams:(NSDictionary *)params {
    if (self != self.interceptor
        && [self.interceptor respondsToSelector:@selector(apiManager:afterLoadRequestWithParams:)]) {
        [self.interceptor apiManager:self afterLoadRequestWithParams:params];
    }
}

#pragma mark - 校验器
// 这里不作实现，这样子类重写时，不需要调用super
- (BOOL)isResponseDataCorrect:(DMResponseModel *)reponseModel {
    return YES;
}

@end
